/*
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <https://www.gnu.org/licenses/>.
 */

#include <QApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QtPlatformHeaders/QXcbWindowFunctions>
#include <QQuickView>
#include <QScreen>
#include <QQuickStyle>
#include <QFile>
#include <QThread>
#include <QTranslator>
#include <initializer_list>
#include "base/launcheritem.h"
#include "base/categoryitem.h"
#include "base/trayiconwatcher.h"
#include "base/sidebarstate.h"
#include "core/modelmanager.h"
#include "core/appmanager.h"
#include "core/desktopwindow.h"
#include "core/appiconimageprovider.h"
#include "utils/getscreenshot.h"
#include "utils/desktopbackground.h"
#include "utils/showclendar.h"
#include "utils/generalsetting.h"
#include "utils/wirelessstatus.h"
#include "utils/wiredlinkstate.h"
#include "utils/accountlogout.h"
#include "utils/blurhelper.h"
#include "logger.h"
#include "kgridview.h"
#include "config.h"
#include "singleton.h"
#ifndef QT_DEBUG
#include "ukui-log4qt.h"
#endif

#include <KWindowSystem/kwindowsystem.h>

static void initContextProperty(std::initializer_list<QQmlEngine *> qmlEngineList)
{
    static common::QmlLogger qmlLogger;
    ModelManager &modelMgr = common::Singleton<ModelManager>::GetInstance();
    static AppManager appMgr;
    static TrayIconWatcher trayIconWatcher;
    static SidebarState sidebarState;
    static ShowClendar showCal;

    static QMap<QString, QObject*> qmlContextPropertyObjs;
    qmlContextPropertyObjs["qmlLogger"] = &qmlLogger;
    qmlContextPropertyObjs["modelManager"] = &modelMgr;
    qmlContextPropertyObjs["appManager"] = &appMgr;
    qmlContextPropertyObjs["trayIconWatcher"] = &trayIconWatcher;
    qmlContextPropertyObjs["sidebarState"] = &sidebarState;
    qmlContextPropertyObjs["showCal"] = &showCal;

    auto it = qmlEngineList.begin();
    qreal physicalDotsPerInch = QGuiApplication::primaryScreen()->physicalDotsPerInch();
    qreal devicePixelRatio = QGuiApplication::primaryScreen()->devicePixelRatio();
    qDebug() << devicePixelRatio;
    while (it != qmlEngineList.end()) {
        auto objIt = qmlContextPropertyObjs.constBegin();
        while (objIt != qmlContextPropertyObjs.constEnd()) {
            (*it)->rootContext()->setContextProperty(objIt.key(), objIt.value());
            ++ objIt;
        }
        (*it)->rootContext()->setContextProperty("screenPixelDensity", physicalDotsPerInch * devicePixelRatio);
        ++ it;
    }
}

static void registerCppTypeToQml()
{
    // qml js:
    // if (type == LauncherItem.AppIcon) {} ...
    qmlRegisterType<LauncherItem>("org.ukui.tablet", 1, 0, "LauncherItem");
    qmlRegisterType<CategoryItem>("org.ukui.tablet", 1, 0, "CategoryItem");
    qmlRegisterType<GetScreenShot>("org.ukui.tablet", 1, 0, "ScreenShot");
    qmlRegisterType<DesktopBackground>("org.ukui.tablet", 1, 0, "Background");
    qmlRegisterType<KQuickGridView>("org.ukui.tablet", 1, 0, "KGridView");
    qmlRegisterType<LauncherItem>("org.ukui.tablet", 1, 0, "LauncherItem");
    qmlRegisterType<CGeneralSetting>("MyGeneralSetting", 1, 0, "CGeneralSetting");
    qmlRegisterType<WirelessStatus>("org.ukui.tablet", 1, 0, "WirelessStatus");
    qmlRegisterType<WiredLinkState>("org.ukui.tablet", 1, 0, "WiredLinkState");
    qmlRegisterType<AccountLogout>("org.ukui.tablet", 1, 0, "AccountLogout");
    qmlRegisterType<BlurHelper>("org.ukui.tablet", 1, 0, "BlurHelper");
    qmlRegisterType<TrayIconWatcher>("org.ukui.tablet", 1, 0, "TrayIconWatcher");
}

void initWindows(DesktopWindow &mainWindow, QQuickView &taskbarWindow)
{
    taskbarWindow.setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint /*| Qt::X11BypassWindowManagerHint*/);
    if (!QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) {
        KWindowSystem::setState(taskbarWindow.winId(), NET::SkipTaskbar | NET::SkipSwitcher | NET::SkipPager);
    }

    taskbarWindow.setModality(Qt::WindowModal);
    taskbarWindow.setTitle("tablet-taskbar");
    taskbarWindow.setColor(Qt::transparent);

    initContextProperty({mainWindow.engine(), taskbarWindow.engine()});
    mainWindow.engine()->addImportPath("qrc:/ui/qml");

    taskbarWindow.engine()->addImportPath("qrc:/ui/qml");

    mainWindow.engine()->addImageProvider("icons", new AppIconImageProvider);
    taskbarWindow.engine()->addImageProvider("icons", new AppIconImageProvider);

    bool openGLEnabled = QQuickView::sceneGraphBackend() == "software" ? false : true;
    mainWindow.engine()->rootContext()->setContextProperty("openGLEnabled", QVariant::fromValue<bool>(openGLEnabled));
    taskbarWindow.engine()->rootContext()->setContextProperty("openGLEnabled", QVariant::fromValue<bool>(openGLEnabled));

    QObject::connect(&mainWindow, &DesktopWindow::desktopIsNotActived, [&mainWindow]() {
        common::Singleton<ModelManager>::GetInstance().setAllItemsState(LauncherItem::Normal);
        if (!mainWindow.rootObject()) {
            qWarning() << "Desktop active changed, but the main window's root object is null.";
            return;
        }
        QMetaObject::invokeMethod(mainWindow.rootObject(), "desktopIsNotActived");
    });

    ModelManager &modelMgr = common::Singleton<ModelManager>::GetInstance();
    QObject::connect(&modelMgr, &ModelManager::desktopDataInited, qApp, [&mainWindow, &taskbarWindow]() {
        QTimer::singleShot(20, qApp, [&taskbarWindow, &mainWindow] {
            taskbarWindow.engine()->addImportPath("qrc:/ui/qml");
            taskbarWindow.setSource(QUrl("qrc:/ui/qml/TaskBar.qml"));

            int counter = 0;
            while (!mainWindow.isSceneGraphInitialized() &&
                   !mainWindow.rootObject() && counter < 10000) {
                qApp->processEvents();
                ++ counter;
                QThread::msleep(1);
            }
            if (!mainWindow.rootObject()) {
                qWarning() << "Main window scene graph has not initialized!";
                if (taskbarWindow.rootObject()) {
                    int screenWidth = taskbarWindow.screen() ? taskbarWindow.screen()->geometry().width() : 1920;
                    int screenHeight = taskbarWindow.screen() ? taskbarWindow.screen()->geometry().height() : 1080;
                    taskbarWindow.rootObject()->setState("Show");
                    double tabletScaleFactor = (screenWidth >= screenHeight ? screenHeight / 1080 : screenWidth / 1080);
                    taskbarWindow.setY(screenHeight - tabletScaleFactor * (36 + 96 + 2) - 4);
                    taskbarWindow.show();
                }
                return;
            }

            QQuickItem *taskBarItem = mainWindow.rootObject()->property("taskBar").value<QQuickItem *>();
            if (!taskBarItem) {
                qWarning() << "Cat not get the taskbar in desktop.";
                return;
            }
            qInfo() << "ukui-tablet-desktop inited1." << taskBarItem->x() << taskBarItem->y()
                    << taskBarItem->width() << taskBarItem->height();
            if (taskbarWindow.rootObject()) {
                taskbarWindow.rootObject()->setState("Show");
                taskbarWindow.setGeometry(qFloor(taskBarItem->x()),
                                          qFloor(taskBarItem->y()),
                                          qCeil(taskBarItem->width()),
                                          qCeil(taskBarItem->height()));
                taskbarWindow.show();
                qDebug() << "taskbar window geometry: " << taskbarWindow.geometry();
            }
        });
    });
}

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif

    QApplication app(argc, argv);

    QTranslator qtTranslator, appTranslator;
    const QString appTranslationFile = qApp->applicationName() + "_" + QLocale::system().name();
    if (qtTranslator.load("qt_" + QLocale::system().name(),
                    QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
       app.installTranslator(&qtTranslator);
    }
#ifndef QT_DEBUG
    const QString transtalionPath = "/usr/share/ukui/translations/ukui-tablet-desktop";
    initUkuiLog4qt("ukui-tablet-desktop");
    if (appTranslator.load(transtalionPath + "/" + appTranslationFile)) {
        app.installTranslator(&appTranslator);
    }
#else
    if (appTranslator.load("../../ukui-tablet-desktop/tablet-desktop/translations/" + appTranslationFile)) {
        app.installTranslator(&appTranslator);
    }
#endif
    else {
        qWarning() << "Load translation file failed." << appTranslationFile;
    }

    DesktopWindow mainWindow;
    PlatformWindow taskbarWindow;
    initWindows(mainWindow, taskbarWindow);

    QQuickStyle::setStyle("org.ukui.style");
    registerCppTypeToQml();


    QObject::connect(&mainWindow, &QQuickWindow::sceneGraphInitialized, []() {
        QDBusMessage message = QDBusMessage::createMethodCall("org.gnome.SessionManager",
                                                              "/org/gnome/SessionManager",
                                                              "org.gnome.SessionManager",
                                                              "startupfinished");
        QList<QVariant> args;
        args.append("ukui-tablet-desktop");
        args.append("startupfinished");
        message.setArguments(args);
        QDBusConnection::sessionBus().send(message);
        qDebug() << "ukui-tablet-desktop start finished";
    });

    auto desktopMode = common::Singleton<Config>::GetInstance().desktopMode();
    if (desktopMode == Config::Tablet) {
        mainWindow.setSource(QUrl("qrc:/ui/qml/Tablet.qml"));
    }
    if (desktopMode == Config::EduScreen) {
        mainWindow.setSource(QUrl("qrc:/ui/qml/EduScreen.qml"));
    }

    mainWindow.setGeometry(mainWindow.screen()->geometry());
    mainWindow.show();
    if (mainWindow.rootObject()) {
        mainWindow.rootObject()->setState("Show");
    }
    return app.exec();
}
